﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Devonline.Core;
using Devonline.Entity;
using Devonline.Http;
using Devonline.Utils;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace Devonline.Identity.Admin.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    [Authorize]
    public class AttachmentsController : ControllerBase
    {
        private readonly IdentityDbContext _context;
        private readonly ILogger<ResourcesController> _logger;

        public AttachmentsController(IdentityDbContext context, ILogger<ResourcesController> logger)
        {
            _context = context;
            _logger = logger;
        }

        /// <summary>
        /// 使用限定的 businessType 和 businessKey 获取附件
        /// </summary>
        /// <param name="businessType"></param>
        /// <param name="businessKey"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> GetAsync(string businessType, string businessKey)
        {
            businessType ??= Request.GetRequestOption<string>(nameof(businessType));
            businessKey ??= Request.GetRequestOption<string>(nameof(businessKey));
            _logger.LogInformation("user {user} query the attachment list with businessType {businessType} and businessKey {businessKey}", User.Identity.Name, businessType, businessKey);
            if (businessType.IsNotNullOrEmpty() || businessKey.IsNotNullOrEmpty())
            {
                var queryable = _context.Attachments.AsQueryable();
                if (businessType.IsNotNullOrEmpty())
                {
                    queryable = queryable.Where(x => x.BusinessType == businessType);
                }

                if (businessKey.IsNotNullOrEmpty())
                {
                    queryable = queryable.Where(x => x.BusinessKey == businessKey);
                }

                var attachments = await queryable.ToListAsync();
                _logger.LogInformation("user {user} query the attachment list with businessType {businessType} and businessKey {businessKey} success", User.Identity.Name, businessType, businessKey);
                return Ok(attachments);
            }

            return NotFound();
        }

        /// <summary>
        /// 文件下载, 提供完整文件相对路径的单个文件下载
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        [HttpGet("Download")]
        public async Task<IActionResult> Download(string fileName)
        {
            fileName ??= Request.GetRequestOption<string>(nameof(fileName));
            if (fileName.IsNullOrEmpty())
            {
                throw new Exception("缺少必须的文件名!");
            }

            string filePath = Request.GetRequestOption<string>(nameof(filePath));
            _logger.LogInformation("user {user} download the file <{fileName}>", User.Identity.Name, fileName);
            filePath = filePath.IsNullOrEmpty() ? fileName : Path.Combine(filePath, fileName);
            var attachment = await _context.GetQueryable<Attachment>().FirstOrDefaultAsync(x => x.Path == filePath).ConfigureAwait(false);
            var fileInfo = new FileInfo((attachment?.Path ?? fileName).GetAttachmentPath());
            if (fileInfo.Exists)
            {
                _logger.LogInformation("user {user} download the file <{fileName}> success", User.Identity.Name, fileName);
                return File(fileInfo.OpenRead(), attachment?.ContentType ?? AppSettings.CONTENT_TYPE_OF_STREAM, attachment?.Name ?? fileName);
            }

            return NotFound();
        }

        /// <summary>
        /// 文件下载, 提供类似于文件夹目录结构的访问方式进行下载
        /// 文件路径只支持一级路径, 适用于访问自动保存的文件或者符合目录规范的文件
        /// </summary>
        /// <param name="filePath">文件路径(只支持一级路径)</param>
        /// <param name="fileName">文件名</param>
        /// <returns></returns>
        [HttpGet("Files/{filePath}/{fileName}")]
        public async Task<IActionResult> Files(string filePath, string fileName)
        {
            _logger.LogInformation("user {user} get the file from path <{filePath}> and name <{fileName}>", User.Identity.Name, filePath, fileName);
            filePath = filePath.IsNullOrEmpty() ? fileName : Path.Combine(filePath, fileName);
            var attachment = await _context.GetQueryable<Attachment>().FirstOrDefaultAsync(x => x.Path == filePath).ConfigureAwait(false);
            var fileInfo = new FileInfo((attachment?.Path ?? filePath).GetAttachmentPath());
            if (fileInfo.Exists)
            {
                _logger.LogInformation("user {user} get the file from path <{filePath}> and name <{fileName}> success", User.Identity.Name, filePath, fileName);
                return File(fileInfo.OpenRead(), attachment?.ContentType ?? AppSettings.CONTENT_TYPE_OF_STREAM, attachment?.Name ?? fileName);
            }

            return NotFound();
        }

        /// <summary>
        /// 文件上传, 支持批量上传, 返回 UploadResult 集合
        /// </summary>
        /// <param name="files"></param>
        /// <returns></returns>
        [HttpPost("Upload")]
        public async Task<IActionResult> Upload(IEnumerable<IFormFile> files)
        {
            if (files.IsNullOrEmpty() && Request.HasFormContentType && Request.Form.Files.IsNotNullOrEmpty())
            {
                files = Request.Form.Files;
            }

            var uploadResults = await files.UploadFilesAsync<Attachment, string>();
            var fileNames = uploadResults.Select(x => x.FileName).ToString<string>();
            var uploadResult = uploadResults.Select(x => x.Result).ToString<string>();
            if (uploadResults.Any(x => x.StatusCode == HttpStatusCode.OK))
            {
                _logger.LogInformation("user {user} upload the files <{uploadFiles}> success with result <{uploadResult}>!", User.Identity.Name, fileNames, uploadResult);
                return Ok(uploadResults);
            }
            else
            {
                _logger.LogWarning("user {user} upload the files <{uploadFiles}> fail with result <{uploadResult}>!", User.Identity.Name, fileNames, uploadResult);
                return BadRequest(uploadResults);
            }
        }

        /// <summary>
        /// 文件删除, 支持由逗号间隔的文件名批量删除
        /// </summary>
        /// <param name="fileNames"></param>
        /// <returns></returns>
        [HttpDelete("Delete")]
        public async Task<IActionResult> Delete(string fileNames)
        {
            fileNames ??= Request.GetRequestOption<string>(nameof(fileNames));
            if (fileNames.IsNullOrEmpty())
            {
                return BadRequest("请输入要删除的文件名!");
            }

            _logger.LogInformation("user {user} will delete the files <{fileNames}>", User.Identity.Name, fileNames);
            var files = fileNames.Split(AppSettings.CHAR_COMMA, StringSplitOptions.RemoveEmptyEntries);
            var attachments = await _context.GetQueryable<Attachment>().Where(x => files.Contains(x.Path)).ToListAsync().ConfigureAwait(false);
            if (attachments.IsNotNullOrEmpty())
            {
                _context.RemoveRange(attachments);
                await _context.SaveChangesAsync().ConfigureAwait(true);
                _logger.LogInformation("user {user} remove the file attachments <{fileNames}> from database success", User.Identity.Name, fileNames);
            }

            if (files.IsNotNullOrEmpty())
            {
                foreach (var file in files)
                {
                    try
                    {
                        var filePath = file.GetAttachmentPath();
                        if (System.IO.File.Exists(filePath))
                        {
                            System.IO.File.Delete(filePath);
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.Exception($"文件 {file} 删除失败!", ex);
                        _logger.LogError(ex, "user {user} remove the file <{file}> throw exception", User.Identity.Name, file);
                    }
                }
            }

            _logger.LogInformation("user {user} remove the attachment files <{fileNames}> from file system success", User.Identity.Name, fileNames);
            return Ok();
        }
    }
}
